using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Configuration;

using KodeIT.Web.Configuration;

namespace KodeIT.Web.Hosting
{
    /// <summary>
    /// This class is the managed instance associated with the native ISAPI Filter during its lifetime.
    /// </summary>
    [ClassInterface(ClassInterfaceType.None)]
    public class ProxyInstance : IProxyInstance
    {
        private List<InstanceEvents> _instanceEventsList;

        #region Properties

        private List<InstanceEvents> InstanceEventsList
        {
            get
            {
                if (null == _instanceEventsList)
                {
                    _instanceEventsList = new List<InstanceEvents>();
                    HttpFiltersSection filtersSection = ConfigurationManager.GetSection("httpFilters") as HttpFiltersSection;

                    foreach (HttpFilterElement httpFilter in filtersSection.Filters)
                    {
                        IHttpFilter filter = Activator.CreateInstance(Type.GetType(httpFilter.Type, true)) as IHttpFilter;
                        InstanceEvents instanceEvents = new InstanceEvents(filter);
                        _instanceEventsList.Add(instanceEvents);
                    }
                }

                return _instanceEventsList;
            }
        }

        #endregion

        #region IProxyInstance Members

        FilterEvent IProxyInstance.Register()
        {
            FilterEvent events = 0;
            try
            {
                foreach (InstanceEvents instanceEvents in InstanceEventsList)
                {
                    try
                    {
                        instanceEvents.Instance.Init(instanceEvents as IFilterEvents);
                        events |= instanceEvents.EnumerateEvents();
                    }
                    catch (Exception exp)
                    {
                        EventHelper.WriteFactory("Unable to register managed filter events", instanceEvents.Instance.GetType(), exp);
                        throw;
                    }
                }
            }
            catch (Exception exp)
            {
                EventHelper.Write("Unable to create the configured managed filters", exp);
                throw;
            }
            return events;
        }

        IProxySession IProxyInstance.CreateSession()
        {
            return new FilterSession() as IProxySession;
        }

        FilterStatus IProxyInstance.OnRawData(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, int rawLength)
        {
            if ((FilterEvent.ReadRawData != eventType) && (FilterEvent.SendRawData != eventType))
            {
                return FilterStatus.NextNotification;
            }

            RawDataEvent context = new RawDataEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType), rawLength);
            RawDataEventArgs eventArgs = new RawDataEventArgs(context);

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    if (eventType == FilterEvent.ReadRawData)
                    {
                        instanceEvents.OnReadRawData(eventArgs);
                    }
                    else
                    {
                        instanceEvents.OnSendRawData(eventArgs);
                    }
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnPreProcHeaders(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType)
        {
            RequestHeadersEvent context = new RequestHeadersEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType));

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnPreProcHeaders(new PreProcHeadersEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnUrlMap(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, int urlLength, int pathLength)
        {
            UrlMapEvent context = new UrlMapEvent(new NativeContext(session, contextPtr, eventPtr, eventType), urlLength, pathLength);

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnUrlMap(new UrlMapEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnAuthentication(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType)
        {
            AuthenticationEvent context = new AuthenticationEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType));

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnAuthentication(new AuthenticationEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnAccessDenied(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, int urlLength, int pathLength, AccessDeniedReason reason)
        {
            AccessDeniedEvent context = new AccessDeniedEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType), urlLength, pathLength, reason);

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnAccessDenied(new AccessDeniedEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnAuthComplete(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, IntPtr token)
        {
            using (AuthCompleteEvent context = new AuthCompleteEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType), token))
            {
                foreach (InstanceEvents instanceEvents in _instanceEventsList)
                {
                    try
                    {
                        instanceEvents.OnAuthComplete(new AuthCompleteEventArgs(context));
                    }
                    catch (Exception exp)
                    {
                        return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                    }
                }

                try
                {
                    return context.Update();
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, exp);
                }
            }
        }

        FilterStatus IProxyInstance.OnSendResponse(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, int httpStatus)
        {
            ResponseHeadersEvent context = new ResponseHeadersEvent(new NativeContext(
                session, contextPtr, eventPtr, eventType), httpStatus);

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnSendResponse(new SendResponseEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnLog(IProxySession session, IntPtr contextPtr, IntPtr eventPtr, FilterEvent eventType, int clientHostNameLength, int clientUserNameLength, int serverNameLength, int operationLength, int targetLength, int parametersLength, int httpStatus, int win32Status, int bytesSent, int bytesRecvd, int msTimeForProcessing)
        {
            LogEvent context = new LogEvent(
                new NativeContext(session, contextPtr, eventPtr, eventType),
                clientHostNameLength, clientUserNameLength, serverNameLength, operationLength, targetLength, parametersLength,
                httpStatus, win32Status, bytesSent, bytesRecvd, msTimeForProcessing);

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    instanceEvents.OnLog(new LogEventArgs(context));
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        FilterStatus IProxyInstance.OnEndRequest(IProxySession session, IntPtr contextPtr, FilterEvent eventType)
        {
            if ((FilterEvent.EndOfRequest != eventType) && (FilterEvent.EndOfNetSession != eventType))
            {
                return FilterStatus.NextNotification;
            }

            ContextEvent context = new ContextEvent(new NativeContext(session, contextPtr, eventType));

            foreach (InstanceEvents instanceEvents in _instanceEventsList)
            {
                try
                {
                    if (eventType == FilterEvent.EndOfRequest)
                    {
                        instanceEvents.OnEndOfRequest(new ContextEventArgs(context));
                    }
                    else
                    {
                        instanceEvents.OnEndOfNetSession(new ContextEventArgs(context));
                    }
                }
                catch (Exception exp)
                {
                    return context.ProcessError(context, eventType, instanceEvents.Instance.GetType(), exp);
                }
            }

            try
            {
                return context.Update();
            }
            catch (Exception exp)
            {
                return context.ProcessError(context, eventType, exp);
            }
        }

        void IProxyInstance.Shutdown()
        {
            foreach (InstanceEvents instanceEvents in InstanceEventsList)
            {
                try
                {
                    instanceEvents.Instance.Dispose();
                }
                catch (Exception exp)
                {
                    EventHelper.WriteFactory("Unable to shutdown managed filter", instanceEvents.Instance.GetType(), exp);
                    throw;
                }
            }
        }

        #endregion
    }
}
